package com.baitiaojun.excel.core.utils;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.read.listener.ReadListener;
import com.alibaba.excel.write.builder.ExcelWriterBuilder;
import com.alibaba.excel.write.builder.ExcelWriterSheetBuilder;
import com.alibaba.excel.write.handler.WriteHandler;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.metadata.style.WriteFont;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import com.baitiaojun.common.utils.convert.CharsetConvertUtils;
import com.baitiaojun.excel.core.functional.ExcelImportFunctional;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * easyexcel对poi进行了重写, 解析效率更高、内存占用更少、数据模型易封装
 */
@Slf4j
public class ExcelUtils {

    public static final Integer CACHE_SIZE = 1000;

    /**
     * 导出excel
     * @param response
     * @param fileName
     * @param data
     * @param clazz
     * @param <T>
     * @throws IOException
     */
    public static <T> void write(HttpServletResponse response, String fileName, List<T> data, Class<T> clazz) throws IOException {
        write(response, fileName, null, data, clazz);
    }

    /**
     * 导出excel
     * @param response
     * @param fileName
     * @param sheetName
     * @param data
     * @param clazz
     * @param <T>
     * @throws IOException
     */
    public static <T> void write(HttpServletResponse response, String fileName, String sheetName, List<T> data, Class<T> clazz) throws IOException {
        ExcelWriterBuilder write = EasyExcel.write(response.getOutputStream(), clazz).autoCloseStream(false).registerWriteHandler(tableStyle());
        ExcelWriterSheetBuilder sheetBuilder;
        if (StringUtils.isBlank(sheetName)) {
            sheetBuilder = write.sheet();
        } else {
            sheetBuilder = write.sheet(sheetName);
        }
        sheetBuilder.doWrite(data);
        response.setContentType("application/vnd.ms-excel;charset=UTF-8");
        response.setHeader("Content-disposition", "attachment;filename=" +
                CharsetConvertUtils.convert(fileName,
                        CharsetConvertUtils.defaultSysCharset(),
                        CharsetConvertUtils.UTF_8));
    }

    /**
     * 读入excel数据
     * @param file
     * @param clazz
     * @param <T>
     * @throws IOException
     */
    public static <T> void read(MultipartFile file, Class<T> clazz, ExcelImportFunctional excelFunctional) throws IOException {
        List<T> list = new ArrayList<>();
        EasyExcel.read(file.getInputStream(), clazz, new ReadListener<T>() {
                    //每读一行数据就会执行invoke
                    @Override
                    public void invoke(T data, AnalysisContext analysisContext) {
                        list.add(data);
                        if (list.size() > CACHE_SIZE) {
                            //缓存数据存入数据库
                            excelFunctional.execute(list);
                            list.clear();
                        }
                    }

                    //所有数据读完以后会执行doAfterAllAnalysed
                    @Override
                    public void doAfterAllAnalysed(AnalysisContext analysisContext) {
                        //缓存数据存入数据库
                        excelFunctional.execute(list);
                        list.clear();
                        log.info("数据导入完毕");
                    }

                    @Override
                    public void onException(Exception exception, AnalysisContext context) {
                        //导入失败的数据记录到日志
                        log.warn("第 {} 行记录导入失败, 原因是: {}", context.getCurrentRowNum(), exception.getMessage());
                    }
                })
                .autoCloseStream(false) // 不要自动关闭，交给 Servlet 自己处理
                .doReadAllSync();
    }

    private static WriteHandler tableStyle() {
        WriteCellStyle headerStyle = new WriteCellStyle();
        headerStyle.setFillBackgroundColor(IndexedColors.WHITE.index);
        WriteFont headerWriteFont = new WriteFont();
        headerWriteFont.setBold(false);
        headerStyle.setWriteFont(headerWriteFont);
        headerStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
        HorizontalCellStyleStrategy styleStrategy = new HorizontalCellStyleStrategy();
        styleStrategy.setHeadWriteCellStyle(headerStyle);
        return styleStrategy;
    }
}
